/**
 * Packet 概念实体、
 * 打包UDP包的工具类方法
 */
package com.jahentao.integrationTest.case03.common;

import java.io.ByteArrayOutputStream;
import java.net.*;

/**
 * @author jahen
 * @date 2016年8月6日
 * @time 下午9:38:45
 * @category Socket编程
 */
public class Packet {
	
	private long time; // 标识屏幕顺序的时间戳
	private int packetNum; // 一屏包的个数
	private int order; // 标识的第几个包
	private byte[] data; // 数据部分
	
	private int OnePart;
	private int remains;
	private static volatile DatagramSocket sender;
	private DatagramPacket uDPPacket;
	private static SocketAddress address;

	/**
	 * 发送的DatagramSocket采用懒加载单例模式（双重检查锁），发送默认端口为9999
	 * @param ip 目标主机IP地址
	 */
	public static DatagramSocket getSenderSocket(String ip) {
		// 同时初始化发送目标地址
		InetAddress inetAddress = null;
		try {
			inetAddress = InetAddress.getByName(ip);
		} catch (UnknownHostException e) {
			System.out.println("填写的目标主机解析错误");
			e.printStackTrace();
		}

		address = new InetSocketAddress(inetAddress, 8888);

		if(sender==null) {
			try {
				synchronized (Packet.class) {
					sender = new DatagramSocket(9999);
				}
			} catch (SocketException e) {
				System.out.println("服务器端默认发送端口9999，被占用");
				e.printStackTrace();
			}
		}
		return sender;
	}
	
	public Packet(long time, int packetNum, int order, byte[] data) {
		this.time = time;
		this.packetNum = packetNum;
		this.order = order;
		this.data = data;
		this.OnePart = data.length / packetNum;
		this.remains = data.length % packetNum;
	}
	/**
	 * 打包成UDP数据包<br>
	 * 策略是打包5份 本机上每份大概58k
	 */
	public Packet packDatagram(String ip){
		try {
			int offset = order*OnePart;
			int length = (order==packetNum-1)? OnePart+remains : OnePart;
			
			// 8 时间戳 4 packetNum 4 order
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
			baos.write(writeLong(time));
			baos.write(writeInt(packetNum));
			baos.write(writeInt(order));
			baos.write(writeInt(length)); // 写数据长度
			baos.write(data, offset, length);
			byte[] wrappedData = baos.toByteArray();
			
			sender = Packet.getSenderSocket(ip);
			uDPPacket = new DatagramPacket(wrappedData, wrappedData.length, address);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return this;
	}
	/**
	 * 写整数
	 */
	private byte[] writeInt(int integer) {
		byte[] integerBytes = new byte[4];
		for(int i=0; i<4; i++)
			integerBytes[i] = (byte) (integer>>((3-i)*8) & 0xff);
		return integerBytes;
	}

	/**
	 * 写长整数<br>
	 * high bytes first 大端模式
	 */
	private byte[] writeLong(long time) {
		byte[] timeBytes = new byte[8];
		for(int i=0; i<8; i++){
			timeBytes[i] = (byte)((time >> ((7-i)*8)) & 0xff);
		}
		return timeBytes;
	}
	/*
	 * 发送UDP数据包
	 */
	public void send() {
		try {
			sender.send(uDPPacket);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public long getTime() {
		return time;
	}
	public void setTime(long time) {
		this.time = time;
	}
	public int getPacketNum() {
		return packetNum;
	}
	public void setPacketNum(int packetNum) {
		this.packetNum = packetNum;
	}
	public int getOrder() {
		return order;
	}
	public void setOrder(int order) {
		this.order = order;
	}
	public byte[] getData() {
		return data;
	}
	public void setData(byte[] data) {
		this.data = data;
	}
}
